/******************************************************************************
 * @file key.c
 * 
 * @par dependencies
 * - bsp_key.h
 * - stdio.h
 * - stdint.h
 * 
 * @author Shawn Li <2339679615@qq.com>
 * 
 * @brief Provide the HAL APIs of Key and corresponding opetions.
 * 
 * @version v0.1 2025-10-15
 * 
 * @date 2025-10-15
 * 
 * @copyright Copyright (c) 2025 Li Shan
 * All rights reserved.
 * 
 * @note 1 tap == 4 spaces!
 *       Code is UTF-8
 * 
******************************************************************************/

#include "bsp_key.h"

//******************************** Defines **********************************//

//******************** Thread_Function ******************//
osThreadId_t key_TaskHandle;
const osThreadAttr_t key_Task_attributes = {
  .name = "key_Task",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityHigh,
}; 
//******************** Thread_Function ******************//


//********************* Queue_Function ******************//
QueueHandle_t key_queue;

//********************* Queue_Function ******************//




//********************* Function Prototypes**************//
/**
 * @brief Scan the key state (non-blocking with debounce/timeout behavior).
 *
 * Samples the configured key GPIO, applies software debounce logic and
 * returns the detected state through the output parameter @p key_value.
 *
 * @param[out] key_value Pointer to a variable that receives the key state
 *                       (KEY_PRESSED or KEY_NOT_PRESSED).
 * 
 * @return key_status_t  KEY_OK          : the key has been pressed.
 *                       KEY_ERRORTIMEOUT: the key has been released.
 */
key_status_t key_scan      (key_press_status_t * key_value)
{
    key_press_status_t key_status_value = KEY_NOT_PRESSED;
    
    if(HAL_GPIO_ReadPin(Key_GPIO_Port, Key_Pin) == GPIO_PIN_RESET)
    {
        key_status_value = KEY_PRESSED;
        *key_value = key_status_value;
        return KEY_OK;
    }
    *key_value = key_status_value;
    
    return KEY_ERRORTIMEOUT;
}

/**
 * @brief This function will check the key press type: short press or long press.
 *
 * Samples the configured key GPIO, applies software debounce logic and
 * returns the detected state through the output parameter @p key_value.
 *
 * @param[out] key_value Pointer to a variable that receives the key state
 *                       (KEY_PRESSED or KEY_NOT_PRESSED).
 * @param[in] short_press_time_threshold  Time threshold (in milliseconds) to
 *                      differentiate between short press and long press.
 * 
 * @return KEY_SHORT_PRESSED: The key was short pressed.
 *         KEY_LONG_PRESSED : The key was long pressed.
 */
key_status_t key_scan_short_long_press(key_press_status_t *key_value, 
                                        uint32_t    short_press_time)
{
    /** Variables **/ 


    key_status_t       ret_key_status =          KEY_OK;  /* Return value       */
    key_press_status_t key_value_temp = KEY_NOT_PRESSED;  /* Temporary variable */
    uint32_t           counter_tick   =               0;  /* Counter for timing */
    /** Variables **/
    
    // 1. Check the key is pressed
    ret_key_status = key_scan(&key_value_temp);
    
    // TBD: critical section！
    
    // 1.1 If pressed, check if it is a short press
    if ( KEY_OK == ret_key_status )
    {
        if ( KEY_PRESSED == key_value_temp )
        {
            // Get the timestamp of the first key pressed
            counter_tick = HAL_GetTick();  
            
            // 1.1.1 Check the duration of the press
            while ( HAL_GetTick() < counter_tick + short_press_time )
            {}
            
            // 1.1.2 After the duration, check the key state again
            ret_key_status = key_scan(&key_value_temp);
            
            if ( KEY_NOT_PRESSED == key_value_temp )
            {
                // 1.1.3 Determined as a short press
                *key_value = KEY_SHORT_PRESSED;
                return KEY_OK;
            }
            else
            {
                // 1.2 Determined as a long press
                *key_value = KEY_LONG_PRESSED;
                
                // 1.1.3 Prevent the release phase from being included 
                // after long press
                while ( KEY_OK == key_scan(&key_value_temp) )
                {}
                
                return KEY_OK;
            }
        }
    }
    
    return ret_key_status;
}

/** 
 * @brief Key handling thread entry function.
 *
 * This FreeRTOS task polls for key events or waits for notifications and
 * dispatches key events (for example, pushing events to @p key_queue or
 * invoking a registered callback). The @p argument parameter is the value
 * passed when the task was created and may be NULL.
 *
 * @param[in] argument Pointer passed to the task (task parameter).
 * 
 * @return void
 */
void         key_task_func                (void *argument)
{
  
  key_status_t       key_ret      =              KEY_OK;
  key_press_status_t key_value    =     KEY_NOT_PRESSED;
  
  key_queue = xQueueCreate( 10, sizeof(key_press_status_t) );
  uint32_t counter_tick = 0;
  if ( NULL == key_queue )
  {
      printf("Key Queue Create Failed\r\n");
  }
  else
  {
      printf("Key Queue Create Success\r\n");
  }
  
  for(;;)
  {
    counter_tick++;
    // printf("Hello Key Thread\r\n");
    
    key_ret = key_scan(&key_value); 
    
    if ( KEY_OK == key_ret )
    {
        if ( KEY_PRESSED == key_value )
        {
            osDelay(10); // Debounce delay
            if ( HAL_GPIO_ReadPin(Key_GPIO_Port, Key_Pin) == GPIO_PIN_RESET )
            {
                printf("Key Pressed!\r\n");
                if ( pdTRUE == xQueueSendToFront(key_queue, &counter_tick, 0) )
                {
                    printf("Send Successfully\r\n");
                }
            }

        }
    }
    if ( KEY_OK != key_ret )
    {
        printf("Key not pressed\r\n");
    }
    
    osDelay(100);
  }
}
//********************* Function Prototypes**************//
//******************************** Defines **********************************//

